package com.sc.nft.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.dao.AdminDaoEcologyPointsRecordsDao;
import com.sc.nft.dao.DaoEcologyPointsDao;
import com.sc.nft.dao.DaoEcologyPointsWalletDao;
import com.sc.nft.dao.DigitalCollectionDao;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.AdminDaoEcologyPointsVO;
import com.sc.nft.entity.vo.AdminManagementRecordPageVO;
import com.sc.nft.enums.DaoEcologyPointsDetailAmountTypeEnum;
import com.sc.nft.enums.DaoEcologyPointsDetailRecordsTypeEnum;
import com.sc.nft.enums.DaoEcologyPointsWalletLogTypeEnum;
import com.sc.nft.enums.PointsExchangeCollectionConsumeTypeEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.DaoEcologyPointsService;
import com.sc.nft.service.DaoEcologyPointsWalletService;
import com.sc.nft.service.EcologyPointsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;

@Slf4j
@RequiredArgsConstructor
@Service
public class DaoEcologyPointsServiceImpl implements DaoEcologyPointsService {

    private final DaoEcologyPointsDao daoEcologyPointsDao;
    private final AdminDaoEcologyPointsRecordsDao adminDaoEcologyPointsRecordsDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final DaoEcologyPointsWalletService daoEcologyPointsWalletService;
    private final EcologyPointsService ecologyPointsService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addFrozenAmountByDaoId(Long daoId, BigDecimal amount, DaoEcologyPointsDetailAmountTypeEnum amountType, DaoEcologyPointsDetailRecordsTypeEnum type, DateTime settleTime) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.isTrue(daoEcologyPointsDao.addFrozenAmountByDaoId(daoId, amount, amountType), () -> new GlobalRunTimeException("增加待释放金额失败"));
        BigDecimal beforeBalance = BigDecimal.ZERO;
        if (amountType == DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE) {
            beforeBalance = oldDaoEcologyPoints.getAmount();
        } else {
            beforeBalance = oldDaoEcologyPoints.getFrozenAmount();
        }

        DaoEcologyPointsDetail daoEcologyPointsDetail = new DaoEcologyPointsDetail();
        daoEcologyPointsDetail.setDaoId(daoId);
        daoEcologyPointsDetail.setAmountType(amountType);
        daoEcologyPointsDetail.setType(type);
        daoEcologyPointsDetail.setAmount(amount);
        daoEcologyPointsDetail.setRealAmount(amount);
        daoEcologyPointsDetail.setBeforeBalance(beforeBalance);
        daoEcologyPointsDetail.setAfterBalance(beforeBalance.add(amount));
        daoEcologyPointsDetail.setIsAdd(Boolean.TRUE);
        daoEcologyPointsDetail.setSettleTime(settleTime);
        daoEcologyPointsDetail.insert();
        return Boolean.TRUE;
    }

    @Override
    public boolean subFrozenAmountByDaoId(Long daoId, BigDecimal amount, DaoEcologyPointsDetailAmountTypeEnum amountType, DaoEcologyPointsDetailRecordsTypeEnum type, DateTime settleTime) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.isTrue(daoEcologyPointsDao.subFrozenAmountByDaoId(daoId, amount, amountType), () -> new GlobalRunTimeException("减少待释放金额失败"));
        BigDecimal beforeBalance = BigDecimal.ZERO;
        if (amountType == DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE) {
            beforeBalance = oldDaoEcologyPoints.getAmount();
        } else {
            beforeBalance = oldDaoEcologyPoints.getFrozenAmount();
        }

        DaoEcologyPointsDetail daoEcologyPointsDetail = new DaoEcologyPointsDetail();
        daoEcologyPointsDetail.setDaoId(daoId);
        daoEcologyPointsDetail.setAmountType(amountType);
        daoEcologyPointsDetail.setType(type);
        daoEcologyPointsDetail.setAmount(amount);
        daoEcologyPointsDetail.setRealAmount(amount);
        daoEcologyPointsDetail.setBeforeBalance(beforeBalance);
        daoEcologyPointsDetail.setAfterBalance(beforeBalance.add(amount));
        daoEcologyPointsDetail.setIsAdd(Boolean.FALSE);
        daoEcologyPointsDetail.setSettleTime(settleTime);
        daoEcologyPointsDetail.insert();
        return Boolean.TRUE;
    }

    @Override
    public void addAmountAndSubFrozenAmount(Long daoId, BigDecimal amount, DaoEcologyPointsDetailRecordsTypeEnum type) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.isTrue(daoEcologyPointsDao.addAmountAndSubFrozenAmount(daoId, amount), () -> new GlobalRunTimeException("增加待释放金额失败"));

        DaoEcologyPointsDetail frozenAmountDetail = new DaoEcologyPointsDetail();
        frozenAmountDetail.setDaoId(daoId);
        frozenAmountDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.RELEASE);
        frozenAmountDetail.setType(DaoEcologyPointsDetailRecordsTypeEnum.RELEASE_TO_AVAILABLE);
        frozenAmountDetail.setAmount(amount);
        frozenAmountDetail.setRealAmount(amount);
        frozenAmountDetail.setBeforeBalance(oldDaoEcologyPoints.getFrozenAmount());
        frozenAmountDetail.setAfterBalance(oldDaoEcologyPoints.getFrozenAmount().subtract(amount));
        frozenAmountDetail.setIsAdd(Boolean.FALSE);
        frozenAmountDetail.setRemark(StrUtil.EMPTY);
        frozenAmountDetail.insert();

        DaoEcologyPointsDetail amountDetail = new DaoEcologyPointsDetail();
        amountDetail.setDaoId(daoId);
        amountDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
        amountDetail.setType(DaoEcologyPointsDetailRecordsTypeEnum.RELEASE_TRANSFER_IN);
        amountDetail.setAmount(amount);
        amountDetail.setRealAmount(amount);
        amountDetail.setBeforeBalance(oldDaoEcologyPoints.getAmount());
        amountDetail.setAfterBalance(oldDaoEcologyPoints.getAmount().add(amount));
        amountDetail.setIsAdd(Boolean.TRUE);
        amountDetail.setRemark(StrUtil.EMPTY);
        amountDetail.insert();

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmount(Long daoId, BigDecimal amount, DaoEcologyPointsDetailRecordsTypeEnum type, String remark) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.isTrue(daoEcologyPointsDao.addAmount(daoId, amount), () -> new GlobalRunTimeException("增加待释放金额失败"));

        DaoEcologyPointsDetail amountDetail = new DaoEcologyPointsDetail();
        amountDetail.setDaoId(daoId);
        amountDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
        amountDetail.setType(type);
        amountDetail.setAmount(amount);
        amountDetail.setRealAmount(amount);
        amountDetail.setBeforeBalance(oldDaoEcologyPoints.getAmount());
        amountDetail.setAfterBalance(oldDaoEcologyPoints.getAmount().add(amount));
        amountDetail.setIsAdd(Boolean.TRUE);
        amountDetail.setRemark(remark);
        amountDetail.insert();

        if (type == DaoEcologyPointsDetailRecordsTypeEnum.SYSTEM_PROCESSING) {
            AdminDaoEcologyPointsRecords adminDaoEcologyPointsRecords = new AdminDaoEcologyPointsRecords();
            adminDaoEcologyPointsRecords.setDaoId(daoId);
            adminDaoEcologyPointsRecords.setAmount(amount);
            adminDaoEcologyPointsRecords.setBeforeBalance(oldDaoEcologyPoints.getAmount());
            adminDaoEcologyPointsRecords.setAfterBalance(oldDaoEcologyPoints.getAmount().add(amount));
            adminDaoEcologyPointsRecords.setIsAdd(Boolean.TRUE);
            adminDaoEcologyPointsRecords.setRemark(remark);
            adminDaoEcologyPointsRecords.insert();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void subAllAmount(Long daoId, BigDecimal amount, DaoEcologyPointsDetailRecordsTypeEnum type, String remark) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.isTrue(daoEcologyPointsDao.subAllAmount(daoId, amount), () -> new GlobalRunTimeException("增加待释放金额失败"));

        DaoEcologyPointsDetail amountDetail = new DaoEcologyPointsDetail();
        amountDetail.setDaoId(daoId);
        amountDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
        amountDetail.setType(type);
        amountDetail.setAmount(amount);
        amountDetail.setRealAmount(amount);
        amountDetail.setBeforeBalance(oldDaoEcologyPoints.getAmount());
        amountDetail.setAfterBalance(oldDaoEcologyPoints.getAmount().subtract(amount));
        amountDetail.setIsAdd(Boolean.FALSE);
        amountDetail.setRemark(StrUtil.EMPTY);
        amountDetail.insert();

        if (type == DaoEcologyPointsDetailRecordsTypeEnum.SYSTEM_PROCESSING) {
            AdminDaoEcologyPointsRecords adminDaoEcologyPointsRecords = new AdminDaoEcologyPointsRecords();
            adminDaoEcologyPointsRecords.setDaoId(daoId);
            adminDaoEcologyPointsRecords.setAmount(amount);
            adminDaoEcologyPointsRecords.setBeforeBalance(oldDaoEcologyPoints.getAmount());
            adminDaoEcologyPointsRecords.setAfterBalance(oldDaoEcologyPoints.getAmount().subtract(amount));
            adminDaoEcologyPointsRecords.setIsAdd(Boolean.FALSE);
            adminDaoEcologyPointsRecords.setRemark(remark);
            adminDaoEcologyPointsRecords.insert();
        }
    }

    @Override
    public Page<AdminDaoEcologyPointsVO> getPage(String daoName, Integer pageNo, Integer pageSize) {
        return daoEcologyPointsDao.getPage(daoName, new Page<>(pageNo, pageSize));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editDaoPoints(Long daoId, Boolean isAdd, String amount, String remark) {
        DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
        Assert.notNull(oldDaoEcologyPoints, () -> new GlobalRunTimeException("dao不存在"));

        if (isAdd) {
            addAmount(daoId, Convert.toBigDecimal(amount), DaoEcologyPointsDetailRecordsTypeEnum.SYSTEM_PROCESSING, remark);
        } else {
            subAllAmount(daoId, Convert.toBigDecimal(amount), DaoEcologyPointsDetailRecordsTypeEnum.SYSTEM_PROCESSING, remark);
        }
    }

    @Override
    public Page<AdminManagementRecordPageVO> managementRecordPage(Long daoId, Boolean isAdd, Date startTime, Date endTime, Integer pageNo, Integer pageSize) {
        return adminDaoEcologyPointsRecordsDao.managementRecordPage(new Page<>(pageNo, pageSize), daoId, isAdd, startTime, endTime);
    }

    /**
     * 燃料兑换nft
     *
     * @param toUserId
     * @param daoId
     * @param applyNum
     * @param collectionId
     * @param needPoints
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pointApplyExchange(Long toUserId, Long daoId, Integer applyNum, Long collectionId, BigDecimal needPoints, Long pointsId, PointsExchangeCollectionConsumeTypeEnum consumeTypeEnum) {
        if (consumeTypeEnum == PointsExchangeCollectionConsumeTypeEnum.POINTS) {
            DaoEcologyPoints oldDaoEcologyPoints = daoEcologyPointsDao.getByDaoId(daoId);
            Assert.isTrue(daoEcologyPointsDao.subAmount(daoId, needPoints),()->new GlobalRunTimeException("扣减可用金额失败"));
            DigitalCollection collection = digitalCollectionDao.getById(collectionId);
            DaoEcologyPointsDetail amountDetail = new DaoEcologyPointsDetail();
            amountDetail.setDaoId(daoId);
            amountDetail.setAmountType(DaoEcologyPointsDetailAmountTypeEnum.AVAILABLE);
            amountDetail.setType(DaoEcologyPointsDetailRecordsTypeEnum.POINT_EXCHANGE_NFT);
            amountDetail.setAmount(needPoints);
            amountDetail.setRealAmount(needPoints);
            amountDetail.setBeforeBalance(oldDaoEcologyPoints.getAmount());
            amountDetail.setAfterBalance(oldDaoEcologyPoints.getAmount().subtract(needPoints));
            amountDetail.setIsAdd(Boolean.FALSE);
            amountDetail.setRemark("兑换【"+collection.getFirstTitle()+"】");
            amountDetail.insert();
        }else {
            EcologyPoints ecologyPoints = ecologyPointsService.getById(pointsId);
            Assert.notNull(ecologyPoints,()->new GlobalRunTimeException("积分不存在"));
            DigitalCollection collection = digitalCollectionDao.getById(collectionId);
            Assert.isTrue(
                    daoEcologyPointsWalletService.adjustAmount(daoId,pointsId,ecologyPoints.getPointsName(),needPoints, DaoEcologyPointsWalletLogTypeEnum.POINT_EXCHANGE_NFT, "兑换【"+collection.getFirstTitle()+"】")
                    ,()->new GlobalRunTimeException("扣减可用金额失败"));
        }

    }
}
